--  --------------------------------------------------------------------------
--  @File:        formatter.adb
--
--  @Project:     Softwarequalität Blatt 3 / Aufgabe 8
--  @Version:     1.0
--  @Created:     2012-11-01
--  @Author:      Benedikt Markt, Aleksander Zotov, Marcel Kranz
--
--  @Description: Package for formatting string outputs;
--
--  @Modifications:

package body Formatter is

   package SU renames Ada.Strings.Unbounded;

   --  -----------------------------------------------------------------------
   --  @Function: Align
   function Align
     (Text : String;
      Alignment : Alignment_Type;
      Field : Natural) return String is
      Length : Float;
   begin
      --  If field is shorter or equals in length of the text, return
      --  text unformatted;
      if Field <= Text'Length then
         return Text;
      end if;
      --  Calculate free space in the field;
      Length := Float (Field - Text'Length);
      --  Create 2 strings of spaces, each with half the length of free space
      --  in the field (one rounded down, other rounded up);
      declare
         Spaces_1 : String (1 .. Integer (Float'Floor (Length / 2.0)))
           := (others => ' ');
         Spaces_2 : String (1 .. Integer (Float'Ceiling (Length / 2.0)))
           := (others => ' ');
      begin
         --  Return the original text with both strings of spaces. The order
         --  depends on alignment type;
         case Alignment is
            when Left => return Text & Spaces_1 & Spaces_2;
            when Center => return Spaces_1 & Text & Spaces_2;
            when Right => return Spaces_1 & Spaces_2 & Text;
         end case;
      end;
   end Align;

   --  -----------------------------------------------------------------------
   --  @Function: Format
   function Format
     (Text : String;
      Values : Values_Array_Type) return String is

      Formatted_Text, Num : SU.Unbounded_String;
      Alignment : Alignment_Type := Left;
      Index, Field : Natural;
      I : Positive := 1;

   begin
      --  Iterate through entire string;
      while I in Text'Range loop
         if Text (I) = '%' then
            I := I + 1;
            --  If string contains 2 '%' symbols in a row, one '%' symbol
            --  is added to the output string;
            if I in Text'Range and then Text (I) = '%' then
               SU.Append (Formatted_Text, '%');
               I := I + 1;
            elsif I in Text'Range and then Text (I) in '0' .. '9' then
               Index := Character'Pos (Text (I)) - 48;
               I := I + 1;
               --  If after the '%' symbol comes a number in range from 0 to 9
               --  and a letter 'C', 'R' or 'L', the Index and Alignment get
               --  according values;
               if I in Text'Range and then (Text (I) = 'L'
                 or Text (I) = 'C'
                 or Text (I) = 'R') then
                  case Text (I) is
                     when 'L' =>
                        Alignment := Left;
                        I := I + 1;
                     when 'C' =>
                        Alignment := Center;
                        I := I + 1;
                     when 'R' =>
                        Alignment := Right;
                        I := I + 1;
                     when others =>
                        Alignment := Left;
                  end case;
               else
                  Alignment := Left;
               end if;
               --  If the next character is a positive number, the value of
               --  Field is set to this number;
               if I in Text'Range and then Text (I) in '0' .. '9' then
                  Field := Character'Pos (Text (I)) - 48;
                  I := I + 1;
                  while I in Text'Range and then Text (I) in '0' .. '9' loop
                     Field := Field * 10 + Character'Pos (Text (I)) - 48;
                     I := I + 1;
                  end loop;
                  --  Add the replaced value of the item to the output string;
                  SU.Append (Formatted_Text, Align (SU.To_String
                    (Values (Index)), Alignment, Field));
               else
                  --  If the next character after 'L', 'C' or 'R' isn't a
                  --  a number, Field will have a value of 0;
                  SU.Append (Formatted_Text, Align (SU.To_String
                    (Values (Index)), Alignment, 0));
               end if;
            end if;
         else
            SU.Append (Formatted_Text, Text (I));
            I := I + 1;
         end if;
         --  Field is reseted;
         Field := 0;
      end loop;
      return SU.To_String (Formatted_Text);
   end Format;

end Formatter;
